﻿#pragma once

#include "ff_utils.h"
#include "../inc/ff_utils_window.h"


#include <windows.h>
#include <wrl.h>
#include <dxgi1_4.h>
#include <d3d12.h>
#include <D3Dcompiler.h>
#include <DirectXMath.h>
#include <DirectXPackedVector.h>
#include <DirectXColors.h>
#include <DirectXCollision.h>
#include <string>
#include <memory>
#include <algorithm>
#include <vector>
#include <array>
#include <unordered_map>
#include <cstdint>
#include <fstream>
#include <sstream>
#include <cassert>
#include "d3d12.h"

#include <DirectXMath.h>
#include <cstdint>
#include <WindowsX.h>
#include <comdef.h>

#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

#pragma comment(lib,"d3dcompiler.lib")
#pragma comment(lib, "D3D12.lib")
#pragma comment(lib, "dxgi.lib")

namespace feifei
{

	class DxException
	{
	public:
		DxException() = default;
		DxException(HRESULT hr, const std::wstring& functionName, const std::wstring& filename, int lineNumber) :
			ErrorCode(hr),
			FunctionName(functionName),
			Filename(filename),
			LineNumber(lineNumber)
		{
		}

		std::wstring ToString()const
		{
			// Get the string description of the error code.
			_com_error err(ErrorCode);
			std::wstring msg = err.ErrorMessage();

			return FunctionName + L" failed in " + Filename + L"; line " + std::to_wstring(LineNumber) + L"; error: " + msg;
		}

		HRESULT ErrorCode = S_OK;
		std::wstring FunctionName;
		std::wstring Filename;
		int LineNumber = -1;
	};
	inline std::wstring AnsiToWString(const std::string& str)
	{
		WCHAR buffer[512];
		MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, buffer, 512);
		return std::wstring(buffer);
	}
#ifndef ThrowIfFailed
#define ThrowIfFailed(x)                                              \
{                                                                     \
    HRESULT hr__ = (x);                                               \
    std::wstring wfn = AnsiToWString(__FILE__);                       \
    if(FAILED(hr__)) { throw DxException(hr__, L#x, wfn, __LINE__); } \
}
#endif
#ifndef ReleaseCom
#define ReleaseCom(x) { if(x){ x->Release(); x = 0; } }
#endif

	extern E_ffState InitD3D();

	static const int SwapChainBufferCount = 2;
	class D3DBase
	{
	protected:
		Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> mCommandList;		// 命令列表
		Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;			// 命令队列
		int mCurrBackBuffer = 0; // 当前后台缓冲区索引
		Microsoft::WRL::ComPtr<IDXGISwapChain> mSwapChain;					// 交换链
		Microsoft::WRL::ComPtr<ID3D12Resource> mSwapChainBuffer[SwapChainBufferCount];	// 交换链缓冲区
		Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;						// 模板/深度缓冲区

		Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mRtvHeap;	// 渲染目标描述符堆,堆中描述符个数与交换链缓冲区个数相同
		Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mDsvHeap;	// 深度/模板描述符堆,堆中描述符个数为1
		
		UINT64 mCurrentFence = 0;

		// 绘制相关 -------------------------------------------------------------
		D3D12_VIEWPORT mScreenViewport; // 视口配置结构体
		D3D12_RECT mScissorRect; // 剪裁矩形配置结构体

		// window 相关 -------------------------------------------------------------
		HWND mhWindow = nullptr; // window handle
		int mWinWidth = 800;
		int mWinHeight = 600;

	public:
		virtual bool Init();
		void SetWindow(HWND hWin, int width, int height)
		{
			mhWindow = hWin;
			mWinWidth = width;
			mWinHeight = height;
		}
		virtual void Draw();	// 更新命令队列
		virtual void OnResize();
		ID3D12Resource* CurrentBackBuffer()const { return mSwapChainBuffer[mCurrBackBuffer].Get(); }
		D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferView()const;
		D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView()const
		{
			return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
		}

	protected:
		void CreateSwapChain();
		void CreateCommandObjects();
		void CreateRtvAndDsvDescriptorHeaps();
		void FlushCommandQueue();
	};
} // end namespace feifei
